import jeringa
import rtems
import itertools
import random
import gdb

# Performs an injection using the symbol table to get info on a local variable or argument.
# Uses a PhysicalInjector to perform the actual injection.
# Can only inject safely if there are valid local variable symbols at the injection point
# (i.e. it's not a function prologue or epilogue).
# USED BY: InjectionRunManager.
# USES: PhysicalInjector.

class SymbolInjector(object):
    def __init__(self):
        self.physicalInjector = PhysicalInjector()

    # Injects a fault in a local variable or argument.
    def inject(self):
        localSymbols = self.getLocalSymbols(gdb.selected_frame().block())

        if localSymbols:
            randomIndex = random.randint(0, len(localSymbols) - 1)
            jeringa.log("Attacking: %s" % localSymbols[randomIndex].name)

            #gdb.lookup_symbol("value")[0].value(gdb.newest_frame())
            localValue = localSymbols[randomIndex].value(gdb.newest_frame())

            return self.injectInVariable(localValue)

    # Flip a bit at a random offset inside a given variable.
    # 'variable' must be a gdb.Value.
    def injectInVariable(self, variable):
        variableAddress = variable.address
        variableSize = variable.type.sizeof

        if variableAddress:  # If the variable is addressable (i.e. not a register)..
            offset = random.randint(0, variableSize - 1)
            self.physicalInjector.injectAt(long(variableAddress) + offset)
            success = True
        else:
            jeringa.log("Target not addressable")
            success = False

        return success

    # Returns a list of variable/argument symbols visible in the local scope, up until
    # the current static block.
    def getLocalSymbols(self, block):
        symbols = []
        predicate = lambda symbol: symbol.is_variable or symbol.is_argument
        filteredBlock = itertools.ifilter(predicate, block)

        if block.superblock:
            for symbol in filteredBlock:
                symbols.append(symbol)

            if not block.superblock.is_static:
                symbols += self.getLocalSymbols(block.superblock)

        return symbols

    # We can only inject at a given PC if there are valid local symbols available.
    # 'injectionPoint' is a number representing the target PC.
    def canInjectAt(self, injectionPC):
        try:
            result = (not gdb.stack_may_be_invalid(injectionPC) and
                      self.getLocalSymbols(gdb.block_for_pc(injectionPC)))
        except:
            # gdb.stack_may_be_invalid may throw an exception if injectionPC points
            # to hand-written assembly.
            result = False

        return result

################################################################################

# Injects a fault somewhere at some thread's stack space.
# This class uses an OS-dependant stackManager to get info
# on the stack.
# USED BY: InjectionRunManager
# USES: RTEMSStackManager, PhysicalInjector.

class StackInjector(object):
    def __init__(self, stackManager):
        self.stackManager = stackManager
        self.physicalInjector = PhysicalInjector()

    def inject(self):
        stackBeginning = self.stackManager.getStackBeginning()
        stackEnd = long(gdb.parse_and_eval("$sp"))

        #0x2000b11d #0x2000aff9 #0x2000b1e1
        injectionAddress = random.randint(stackEnd, stackBeginning) #0x2000b170, 0x2000b329, 0x2000b2d0

        self.physicalInjector.injectAt(injectionAddress)

        return True # Always success

    def canInjectAt(self, injectionPC):
        return True  # We can successfully inject at any PC.

################################################################################

# Performs a bit flip at a random bit inside the 8-bit byte at the given address.
# 'address' must be a number.
# USED BY: StackInjector, SymbolInjector.
class PhysicalInjector(object):
    def injectAt(self, address):
        #2 #3 #5
        randomBit = random.randint(0, 7)  # Assuming 8-bit byte addressing #5 #0, 6

        jeringa.log("Voy a atacar: %#x; bit %d" % (address, randomBit))
        gdb.execute("set *%d ^= (1 << %d)" % (address, randomBit))
